/* $Id:person.cc $ */
/*
   Copyright (C) 2007 by Jurjen Stellingwerff <jurjen@stwerff.xs4all.nl>
   Part of the Moros Project http://moros.sourceforge.net/

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 3.
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY.

   See the COPYING file for more details.
*/

#define DEBUG_ROUTINE "person"

#include <sstream>
#include <fstream>
#include "../general/util.h"
#include "../general/log.h"
#include "../general/channel.h"
#include "person.h"
#include "race.h"
#include "background.h"
#include "skill.h"
#include "special.h"
#include "stat.h"
#include "item.h"
#include "army.h"
#include "person.h"
#include "../general/general.h"

person::person() {
    name="";
    user="";
    race=0;
    background=map<string,class background_level>();
    skills=map<string,class skill_level>();
    specials=map<string,class has_special>();
    stats=map<string,class stat_amount>();
    items=map<string,class person_items>();
    status=map<string,class status>();
    points=0;
    relations=map<string,class relations>();
    location=0;
    start=0;
    last=0;
}

// Field : name

const string person::getName() const { return name; }
void person::setName(const string v) { name=v; }

// Field : user

const string person::getUser() const { return user; }
void person::setUser(const string v) { user=v; }

// Field : race

class race& person::getRace() const { if (race==0) throw nullpointer(); return *race; }
void person::setRace(class race &v) { race=&v; }

// Field : background

class background_level& person::getBackground(const string v) { return background[v]; }
list<class background_level>& person::listBackground() {
    static list<class background_level> res;
    res.clear();
    loopMap(background,v,class background_level) {
        res.push_back(*v);
    }
    res.sort();
    return res;
}
void person::setBackground(class background_level &v) {
    background[v.getBackground().getName()]=v;
}
void person::delBackground(const string v) { background.erase(v); }
const bool person::isBackground(const string v) const { return background.count(v)>0; }

// Field : skills

class skill_level& person::getSkills(const string v) { return skills[v]; }
map<string,class skill_level>& person::listSkills() { return skills; }
void person::setSkills(class skill_level &v) {
    skills[v.getSkill().getName()]=v;
}
void person::delSkills(const string v) { skills.erase(v); }
const bool person::isSkills(const string v) const { return skills.count(v)>0; }

// Field : specials

class has_special& person::getSpecials(const string v) { return specials[v]; }
map<string,class has_special>& person::listSpecials() { return specials; }
void person::setSpecials(class has_special &v) {
    specials[v.getSpecial().getName()]=v;
}
void person::delSpecials(const string v) { specials.erase(v); }
const bool person::isSpecials(const string v) const { return specials.count(v)>0; }

// Field : stats

class stat_amount& person::getStats(const string v) { return stats[v]; }
map<string,class stat_amount>& person::listStats() { return stats; }
void person::setStats(class stat_amount &v) {
    stats[v.getStat().getName()]=v;
}
void person::delStats(const string v) { stats.erase(v); }
const bool person::isStats(const string v) const { return stats.count(v)>0; }

// Field : items

class person_items& person::getItems(const string v) { return items[v]; }
list<class person_items>& person::listItems() {
    static list<class person_items> res;
    res.clear();
    loopMap(items,v,class person_items) {
        res.push_back(*v);
    }
    res.sort();
    return res;
}
void person::setItems(class person_items &v) {
    items[v.getType().getName()]=v;
}
void person::delItems(const string v) { items.erase(v); }
const bool person::isItems(const string v) const { return items.count(v)>0; }

// Field : status

class status& person::getStatus(const string v) { return status[v]; }
map<string,class status>& person::listStatus() { return status; }
void person::setStatus(class status &v) {
    status[v.getArmy().getName()]=v;
}
void person::delStatus(const string v) { status.erase(v); }
const bool person::isStatus(const string v) const { return status.count(v)>0; }

// Field : points

const int person::getPoints() const { return points; }
void person::setPoints(const int v) { points=v; }

// Field : relations

class relations& person::getRelations(const string v) { return relations[v]; }
map<string,class relations>& person::listRelations() { return relations; }
void person::setRelations(class relations &v) {
    relations[v.getWho().getName()]=v;
}
void person::delRelations(const string v) { relations.erase(v); }
const bool person::isRelations(const string v) const { return relations.count(v)>0; }

// Field : location

const int person::getLocation() const { return location; }
void person::setLocation(const int  v) { location=v; }
const string person::showLocation() const {
    stringstream res;
    res << "(" << (location & 0xffff) << "," << (location << 16) << ")";
    return res.str();
}

// Field : start

const int person::getStart() const { return start; }
void person::setStart(const int v) { start=v; }

// Field : last

const int person::getLast() const { return last; }
void person::setLast(const int v) { last=v; }

void person::save(class channel &ch) {
    ch.write("person", "");
    ch.write("n", getName());
    ch.write("u", getUser());
    ch.write("r", getRace().getName());
    loopPersonBackground(this, s_background) {
        s_background->save(ch);
    }
    loopPersonSkills(this, s_skills) {
        s_skills->save(ch);
    }
    loopPersonSpecials(this, s_specials) {
        s_specials->save(ch);
    }
    loopPersonStats(this, s_stats) {
        s_stats->save(ch);
    }
    loopPersonItems(this, s_items) {
        s_items->save(ch);
    }
    loopPersonStatus(this, s_status) {
        s_status->save(ch);
    }
    ch.write("p", getPoints());
    loopPersonRelations(this, s_relations) {
        s_relations->save(ch);
    }
    ch.write("l", getLocation());
    ch.write("st13", getStart());
    ch.write("la", getLast());
}

void person::load(class channel &ch) {
    if (ch.key=="n" || ch.key=="name") { setName(ch.value); ch.next(); }
    if (ch.key=="u" || ch.key=="user") { setUser(ch.value); ch.next(); }
    if (ch.key=="r" || ch.key=="race") {
        if (!race_repo.isRace(ch.value)) {
            dbg_state("link unknown race %s", ch.value.c_str());
            class race rec;
            rec.setName(ch.value);
            race_repo.setRace(rec);
        }
        setRace(race_repo.getRace(ch.value));
        ch.next();
    }
    while (ch.key=="background_level" || ch.key=="background") {
        ch.next();
        class background_level rec;
        rec.load(ch);
        setBackground(rec);
    }
    while (ch.key=="skill_level" || ch.key=="skills") {
        ch.next();
        class skill_level rec;
        rec.load(ch);
        setSkills(rec);
    }
    while (ch.key=="has_special" || ch.key=="specials") {
        ch.next();
        class has_special rec;
        rec.load(ch);
        setSpecials(rec);
    }
    while (ch.key=="stat_amount" || ch.key=="stats") {
        ch.next();
        class stat_amount rec;
        rec.load(ch);
        setStats(rec);
    }
    while (ch.key=="person_items" || ch.key=="items") {
        ch.next();
        class person_items rec;
        rec.load(ch);
        setItems(rec);
    }
    while (ch.key=="status" || ch.key=="status") {
        ch.next();
        class status rec;
        rec.load(ch);
        setStatus(rec);
    }
    if (ch.key=="p" || ch.key=="points") { setPoints(atoi(ch.value.c_str())); ch.next(); }
    while (ch.key=="relations" || ch.key=="relations") {
        ch.next();
        class relations rec;
        rec.load(ch);
        setRelations(rec);
    }
    if (ch.key=="l" || ch.key=="location") { setLocation(atoi(ch.value.c_str())); ch.next(); }
    if (ch.key=="st13" || ch.key=="start") { setStart(atoi(ch.value.c_str())); ch.next(); }
    if (ch.key=="la" || ch.key=="last") { setLast(atoi(ch.value.c_str())); ch.next(); }
}

void person::xml(ofstream &f, int l, int r) {
    f << ncpy(l, "  ") << "<person name=\"" << xml_encode(getName()) <<
    "\" user=\"" << xml_encode(getUser()) <<
    "\" points=\"" << getPoints() <<
    "\" location_x=\"" << (getLocation() & 0xffff) <<
    "\" location_y=\"" << (getLocation() << 16) <<
    "\" start=\"" << getStart() <<
    "\" last=\"" << getLast() <<
    "\">" << endl;
    if (r<2) try {
        getRace().xml(f,l+1,r+1);
    } catch (nullpointer n) {}
    if (r==0) {
        loopPersonBackground(this, s_background) {
            s_background->xml(f,l+1);
        }
        loopPersonSkills(this, s_skills) {
            s_skills->xml(f,l+1);
        }
        loopPersonSpecials(this, s_specials) {
            s_specials->xml(f,l+1);
        }
        loopPersonStats(this, s_stats) {
            s_stats->xml(f,l+1);
        }
        loopPersonItems(this, s_items) {
            s_items->xml(f,l+1);
        }
        loopPersonStatus(this, s_status) {
            s_status->xml(f,l+1);
        }
        loopPersonRelations(this, s_relations) {
            s_relations->xml(f,l+1);
        }
    }
    f << ncpy(l, "  ") << "</person>" << endl;
}

// Repository

personRepository person_repo;

class person& personRepository::getPerson(const string v) { return persons[v]; }
void personRepository::setPerson(class person& v) {
    if (v.getName()=="") throw unnamed();
    persons[v.getName()]=v;
}
map<string,class person>& personRepository::listPerson() { return persons; }
void personRepository::delPerson(const string v) { persons.erase(v); }
const bool personRepository::isPerson(const string v) const { return persons.count(v)>0; }
void personRepository::save(class channel &ch) {
    loopPersons(rec) {
        rec->save(ch);
    }
}

bool personRepository::load(class channel &ch) {
    if (ch.key=="person") {
        ch.next();
        class person rec;
        rec.load(ch);
        setPerson(rec);
        return true;
    }
    return false;
}
